package com.jiangnan.service;

import com.alibaba.fastjson.JSONObject;
import com.jiangnan.constants.Constants;
import com.jiangnan.model.TableData;
import com.jiangnan.queue.PacketQueue;
import com.jiangnan.receiver.PacketReceiverImpl;
import com.jiangnan.utils.*;
import jpcap.JpcapCaptor;
import jpcap.JpcapWriter;
import jpcap.NetworkInterface;
import jpcap.packet.Packet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;

/**
 * 数据包服务
 * @author chenliang
 * @email wschenliang@aliyun.com
 */
public class PacketService {
    private final static Logger logger= LoggerFactory.getLogger(PacketService.class);
    private static PacketService packetService;

    private PacketService() {
    }

    public static PacketService newInstance() {
        if (packetService == null) {
            packetService = new PacketService();
        }
        return packetService;
    }

    public String getInfoString(String modelStr, String infoStr) {
        HashMap<String, Object> map = new LinkedHashMap<>();
        if (StringUtils.isNotBlank(modelStr)) {
            JSONObject jsonObject = JSONObject.parseObject(modelStr);
            Long time = FastjsonUtils.get(jsonObject, long.class, "time");
            map.put("时间", DateUtil.valueOf(time));
            map.put("caplen", FastjsonUtils.get(jsonObject, "caplen"));
            map.put("协议", FastjsonUtils.get(jsonObject, "proto"));
            map.put("源地址", FastjsonUtils.get(jsonObject, "src_ip") + ":" + FastjsonUtils.get(jsonObject, "src_port"));
            map.put("目的地址", FastjsonUtils.get(jsonObject, "dst_ip") + ":" + FastjsonUtils.get(jsonObject, "dst_port"));
            map.put("源物理地址", FastjsonUtils.get(jsonObject, "src_mac"));
            map.put("目的物理地址", FastjsonUtils.get(jsonObject, "dst_mac"));
            map.put("源真实地址", FastjsonUtils.get(jsonObject, "src_area"));
            map.put("目的真实地址", FastjsonUtils.get(jsonObject, "dst_area"));
            map.put("ethType", FastjsonUtils.get(jsonObject, "ethType"));
        }
        map.put("源数据", infoStr);
        return MapUtils.toString(map, ":  ");
    }

    public String getInfoString2(String modelStr, String infoStr) {
        HashMap<String, Object> map = new LinkedHashMap<>();
        if (StringUtils.isNotBlank(modelStr)) {
            map = JSONObject.parseObject(modelStr, LinkedHashMap.class);
        }
        return MapUtils.toString(map, ":  ");
    }

    public void saveFile(String saveModel, Component parent) {
        JFileChooser fileChooser = SwingUtil.getFileChooser(Constants.FILE_CHOOSER_SAVE);
        int val = fileChooser.showSaveDialog(parent);
        if (val != JFileChooser.APPROVE_OPTION) {
            LogUtils.log("保存文件取消");
            return;
        }
        File file = fileChooser.getSelectedFile();
        if (Constants.SAVA_FILE_JSON.equals(saveModel)) {
            saveJsonFile(file);
        } else {
            savePcapFile(file);
        }
    }

    public void openFile(boolean isAppend, Component parent) {
        //文件选择器
        JFileChooser fileChooser = SwingUtil.getFileChooser(Constants.FILE_CHOOSER_OPEN);
        int val = fileChooser.showOpenDialog(parent);
        if (val != JFileChooser.APPROVE_OPTION) {
            LogUtils.log("打开文件取消");
            return;
        }
        if (!isAppend) {
            //不是追加文件
            JTableUtil.clearJTable();
        }
        File file = fileChooser.getSelectedFile();
        //文件读取操作
        String absolutePath = file.getAbsolutePath();
        JpcapCaptor captor = null;
        try {
            captor = JpcapCaptor.openFile(absolutePath);
        } catch (IOException exception) {
            exception.printStackTrace();
        }
        if (captor == null) {
            logger.error("打开文件捕获器生成失败");
            return;
        }
        DefaultTableModel model = JTableUtil.getTableModel();
        while (true) {
            Packet packet = captor.getPacket();
            if (packet == null) break;
            TableData tableData = PacketUtil.convertPacket2TableData(packet);
            model.addRow(tableData.getDataArrays());
        }
    }

    public void openFileAsyn(boolean isAppend, Component parent) {
        //文件选择器
        JFileChooser fileChooser = SwingUtil.getFileChooser(Constants.FILE_CHOOSER_OPEN);
        int val = fileChooser.showOpenDialog(parent);
        if (val != JFileChooser.APPROVE_OPTION) {
            LogUtils.log("打开文件取消");
            return;
        }
        if (!isAppend) {
            //不是追加文件
            JTableUtil.clearJTable();
        }
        File file = fileChooser.getSelectedFile();
        packetService.readPcapFileAsyn(file);
        DefaultTableModel model = JTableUtil.getTableModel();
        while (true) {
            Packet packet = PacketQueue.deQueue();
            if (packet == null) break;
            TableData tableData = PacketUtil.convertPacket2TableData(packet);
            model.addRow(tableData.getDataArrays());
        }
        LogUtils.log("读取文件成功");
    }

    public void saveJsonFile(File file) {
        LinkedList<String> models = JTableUtil.getModelsFromJTable();
        StringBuilder sb = new StringBuilder();
        for (String model : models) {
            sb.append(model).append("\n");
        }
        try {
            FileUtils.writeFile(file, sb.toString());
        } catch (IOException exception) {
            LogUtils.log("写入文件失败:" + exception.getMessage());
            return;
        }
        LogUtils.log("写入json文件成功");
    }

    private JpcapCaptor captor = null;
    //启动
    public void startCaptureLoop(String deviceIp) {
        if (StringUtils.isBlank(deviceIp)) {
            LogUtils.log("启动失败，未设置网卡设备");
            return;
        }
        NetworkInterface device = JpcapUtil.findDeviceByIP(deviceIp);
        LogUtils.log("启动中...");
        captor = JpcapUtil.getCaptor(device);
        if (captor == null) {
            //可能因为报错为空
            return;
        }
        //开启线程2执行循环监听捕获数据，注意线程2是无法使用线程1的本地线程栈内容的，因此，想要保存数据得传递过去
        new Thread(() -> captor.loopPacket(-1, new PacketReceiverImpl())).start();
        LogUtils.log("启动成功，正在捕获...");
    }
    //停止
    public void stopCaptureLoop() {
        if (captor == null) {
            LogUtils.log("captor为null");
            return;
        }
        captor.breakLoop();
        LogUtils.log("已停止捕获");
    }

    //读取的文件可能很大，所以要单独开个线程一边读，主线程只管获取
    public void readPcapFileAsyn(File file) {
        String absolutePath = file.getAbsolutePath();
        try {
            captor = JpcapCaptor.openFile(absolutePath);
        } catch (IOException exception) {
            LogUtils.log(exception.getMessage() + "注意是否中文路径");
            return;
        }
        while (true) {
            Packet packet = captor.getPacket();
            if (packet == null || packet.data == null) break;
            PacketQueue.enQueue(packet);
        }
//        new Thread(() -> {
//
//
//        }).start();
    }

    //保存文件
    public void savePcapFile(File file) {
        //获取插入的所有文件
        LinkedList<Packet> packets = JTableUtil.getPacketsFromJTable();
        if (CollectionUtils.isEmpty(packets)) {
            return;
        }
        String absolutePath = file.getAbsolutePath();
        try (JpcapWriter writer = JpcapWriter.openDumpFile(captor, absolutePath)) {
            for (Packet packet : packets) {
                writer.writePacket(packet);
            }
            LogUtils.log("保存文件成功");
        } catch (IOException exception) {
            LogUtils.log(exception.getMessage());
        }
    }

}
